package ar.fiuba.logger.formatter;

import java.util.ArrayList;
import java.util.List;

import ar.fiuba.logger.LogFormatterInterface;
import ar.fiuba.logger.LogMessageInterface;
import ar.fiuba.logger.formatRules.DateFormatRule;
import ar.fiuba.logger.formatRules.FileNameFormatRule;
import ar.fiuba.logger.formatRules.FormatRuleInterface;
import ar.fiuba.logger.formatRules.LineNumberFormatRule;
import ar.fiuba.logger.formatRules.LogLevelNameFormatRule;
import ar.fiuba.logger.formatRules.LogMessageFormatRule;
import ar.fiuba.logger.formatRules.LoggerNameFormatRule;
import ar.fiuba.logger.formatRules.MethodNameFormatRule;
import ar.fiuba.logger.formatRules.PercentSignEscapeFormatRule;
import ar.fiuba.logger.formatRules.SeparatorFormatRule;
import ar.fiuba.logger.formatRules.ThreadNameFormatRule;

/**
 * The Class LogFormatter.
 * 
 * This class applies all the format rules to a log message.
 */
public class LogTextFormatter implements LogFormatterInterface {

  /** The rules. */
  private List<FormatRuleInterface> rules;

  /** The format. */
  private String format;

  /** The separator. */
  private String separator;

  /**
   * Instantiates a new log formatter.
   * 
   * @param format the format
   * @param separator the separator
   */
  public LogTextFormatter(String format, String separator) {
    this.init(format, separator);
  }

  /* (non-Javadoc)
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    LogTextFormatter other = (LogTextFormatter) obj;
    if (format == null) {
      if (other.format != null)
        return false;
    } else if (!format.equals(other.format))
      return false;
    if (rules == null) {
      if (other.rules != null)
        return false;
    } else if (!rules.equals(other.rules))
      return false;
    if (separator == null) {
      if (other.separator != null)
        return false;
    } else if (!separator.equals(other.separator))
      return false;
    return true;
  }

  /*
   * (non-Javadoc)
   * 
   * @see ar.fiuba.logger.LogFormatterInterface#format(ar.fiuba.logger.LogMessageInterface)
   */
  @Override
  public String format(LogMessageInterface logMessage) {

    String formattedMessage = format;

    for (int i = rules.size() - 1; i >= 0; i--) {
      formattedMessage = rules.get(i).applyFormatRule(formattedMessage, logMessage);
    }

    return formattedMessage;
  }

  /* (non-Javadoc)
   * @see java.lang.Object#hashCode()
   */
  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((format == null) ? 0 : format.hashCode());
    result = prime * result + ((rules == null) ? 0 : rules.hashCode());
    result = prime * result + ((separator == null) ? 0 : separator.hashCode());
    return result;
  }

  /**
   * Inits the Log Formatter.
   * 
   * @param format the format
   * @param separator the separator
   */
  private void init(String format, String separator) {
    this.format = format;
    this.separator = separator;

    /*
     * Format Rules are added in the reverse order in which I want them to be applied.
     * 
     * WARNING: The order DOES matters! For example: PercentSignEscapeFormatRule must be applied
     * after LogMessageFormatRule. Not the other way!
     */
    rules = new ArrayList<FormatRuleInterface>();
    rules.add(new PercentSignEscapeFormatRule());
    rules.add(new LogMessageFormatRule());
    rules.add(new LogLevelNameFormatRule());
    rules.add(new SeparatorFormatRule(this.separator));
    rules.add(new FileNameFormatRule());
    rules.add(new LineNumberFormatRule());
    rules.add(new MethodNameFormatRule());
    rules.add(new DateFormatRule());
    rules.add(new ThreadNameFormatRule());
    rules.add(new LoggerNameFormatRule());
  }
}
